Описанный вами подход подойдет далеко не всем проектам. Он может быть уместен для домашнего пет-проекта или коробочного коммерческого продукта, который не планируется поддерживать в будущем — сделал и отдал.
Но для продуктовой разработки он совершенно не подходит. Особенность такой разработки — в постоянных изменениях кодовой базы по мере добавления новых функциональностей. Главной задачей разработчика становится поддержание evolvability — способности системы к изменениям. Это означает, что фича одинаковой сложности на первом и пятом году жизни сервиса будет требовать примерно одинаковых усилий. То есть сервис с годами не превращается в монолитный, неподдерживаемый ком.
Ошибочно ставить во главу угла только скорость выполнения тестов — не менее важна и глубина обратной связи. Интеграционный тест дает полноценное представление о работе приложения. Юнит-тест же сообщает лишь о корректности одного из сотен или тысяч классов и ничего не говорит о работоспособности фичи в целом.
Сильный упор на юнит-тесты ведет к цементированию кода: значимый рефакторинг становится дорогим, потому что изменение дизайна тянет за собой переписывание множества тестов. А если тесты меняются следом за кодом, можно ли им доверять? О хрупкости кода при чрезмерном использовании юнит-тестов много пишет Владимир Хориков в книге Принципы юнит-тестирования.
На мой взгляд, при тестировании микросервисов в продуктовой разработке приоритет следует отдавать интеграционным тестам. Вот хорошая статья на эту тему: https://engineering.atspotify.com/2018/01/testing-of-microservices/
Интеграционные тесты никак не могут помешать рефакторингу, так как они не привязаны ни к дизайну кода, ни к контрактам методов и классов — в отличие от юнит-тестов. То есть интеграционный тест веб-сервиса — это отправить JSON в HTTP-эндпоинт и проверить ответ. У вас полная свобода для рефакторинга: можно даже заменить Java на Kotlin, и грамотно написанный тест вам не помешает, а наоборот — поможет, проверив контракт сервиса.
То, что юнит-тесты якобы способствуют написанию менее связанного кода, само по себе требует подтверждения и не вытекает из самой природы этих тестов.
Теперь о фидбэке от тестов. Уточню, что речь идёт о разработке сервисов в продуктовой разработке, где фичи скорее сквозные — проходят через все слои. Есть два варианта:
1. Если вы не используете TDD, то обратная связь от тестов игнорируется на большей части разработки. То есть это уже не та обратная связь, о которой идёт речь.
2. Если вы используете TDD, то до этапа рефакторинга код по определению не имеет устоявшегося дизайна и структуры. Значит, юнит-тесты либо будут мешать — «цементируя» драфтовый код, либо будут фактически превращены в интеграционные.
И вот какой вывод я делаю: если мы всерьёз говорим о важности обратной связи от тестов, то речь, скорее всего, идёт про TDD. А в случае сквозных фичей в продуктовых сервисах только интеграционные тесты позволяют не мешать разработчику писать код, а помогать — проверяя контракт.
> эти тесты не проверяют внутреннюю логику и взаимодействие разных модулей
И да и нет. Они не проверяют особенность реализации, но проверяют контракт. Функциональные и нефункциональные требования предъявляются к контракту сервиса. Внутреннее устройство не должно трактовать условия, а служить средством достижения ожидаемого результата. Часто выходит так, что совершенно не важно кто, кого и как вызывает внутри кода, если требования выполняются.
По моему опыту, акцент на интеграционные тесты снижал количество багов и уменьшал time to market. В итоге мы пришли к тому, что сервисы даже не запускались локально для финальной проверки через условный Postman — хватало глубоких интеграционных тестов.
ps: я не утверждаю, что юнит тесты не нужны. Отличное применение им - это проверка маперов, сложной бизнес логики, race condition проверок.
Связано с Testing trophy.